package com.eds.service.impl;

import com.alibaba.nacos.client.utils.StringUtils;
import com.eds.domain.CourseDocument;
import com.eds.domain.CourseSearchDTO;
import com.eds.feign.CourseFeign;
import com.eds.service.SearchService;
import com.pojo.Course;
import lombok.SneakyThrows;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.AnalyzeRequest;
import org.elasticsearch.client.indices.AnalyzeResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 *
 * @ClassName：SearchServiceImpl
 * @Author: vvSmile
 * @Date: 2025/04/11
 * @Description: 必须描述类做什么事情, 实现什么功能
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private CourseFeign courseFeign;
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    private ElasticsearchRestTemplate template;


    @Override
    public void asyncInitEsData() {
        //获取MySQL中的数据
        List<Course> courses = courseFeign.selectAllCourseList();
        //将List<Course>转换成List<CourseIndex>
        ArrayList<CourseDocument> courseDescs = new ArrayList<>();
        //遍历courses，将Course转换成CourseIndex
        courses.forEach(course -> {
            //获取课程标签
            List<String> tags = analyze(course.getTitle(), "ik_smart");
            //将Course转换成CourseIndex
            CourseDocument courseDocument = CourseDocument.fromCourse(course, tags);
            //将CourseIndex添加到courseDescs中
            courseDescs.add(courseDocument);
        });
        //使用SpringDataElasticsearch将List<CourseIndex>数据批量导入ES中
        template.save(courseDescs);
    }


    //词条生成方法：获取指定文档的词条数据
    @SneakyThrows
    public List<String> analyze(String name, String ikSmart) {
        //封装请求数据
        AnalyzeRequest request = AnalyzeRequest.withIndexAnalyzer("course", ikSmart, name);
        //分词
        AnalyzeResponse response = client.indices().analyze(request, RequestOptions.DEFAULT);
        //封装结果
        ArrayList<String> tags = new ArrayList<>();
        response.getTokens().forEach(token -> {
            tags.add(token.getTerm());
        });
        return tags;
    }


    /* *
     * @Title: autoSuggest
     * @Author: vvSmile
     * @Date: 2025-04-13 17:19:05
     * @param keyword
     * @return List<String>
     * @Description: TODO 自动补全
     */
    @Override
    public List<String> autoSuggest(String keyword) throws IOException {
        //1.封装查询条件
        SearchRequest searchRequest = new SearchRequest("course");
        //1.1创建补全建议构建器
        CompletionSuggestionBuilder completionSuggestBuilder = SuggestBuilders
                .completionSuggestion("titleSuggest")
                .prefix(keyword)
                .skipDuplicates(true)
                .size(10);
        //1.2创建建议构建器
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        suggestBuilder.addSuggestion("prefix_suggest", completionSuggestBuilder);
        //1.3设置搜索源
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.suggest(suggestBuilder);
        searchRequest.source(searchSourceBuilder);
        //2.调用ES的client中的Search方法
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        //3.封装结果
        if (searchResponse.getSuggest() != null) {
            //3.1获取结果
            List<String> result = new ArrayList<>();
            Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> suggestion = searchResponse.getSuggest().getSuggestion("prefix_suggest");
            suggestion.forEach(entry -> {
                entry.getOptions().forEach(option -> {
                    result.add(option.getText().toString());
                });
            });
            return result;
        }
        return null;
    }


    /* *
     * @Title: searchCourse
     * @Author: vvSmile
     * @Date: 2025-04-13 18:32:52
     * @param keyword
     * @return List<CourseDocument>
     * @Description: TODO 搜索课程
     */
    @Override
    public PageImpl<CourseDocument> searchCourse(CourseSearchDTO courseSearchDTO) {
        //1.封装查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //添加查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //关键词搜索
        if (StringUtils.isNotBlank(courseSearchDTO.getKeyword())) {
            //1.1添加关键词搜索
            boolQuery.should(QueryBuilders.multiMatchQuery(courseSearchDTO.getKeyword())
                    .field("title", 3.0f)
                    .field("details", 1.0f)
                    .type(MultiMatchQueryBuilder.Type.BEST_FIELDS));
            //1.2添加课程标签搜索
            boolQuery.should(QueryBuilders.matchQuery("titleSuggest", courseSearchDTO.getKeyword()).boost(2.0f));
            //设置最小匹配输
            boolQuery.minimumShouldMatch(1);
            //1.3添加课程分类搜索
            if (courseSearchDTO.getCategoryId() != null) {
                boolQuery.filter(QueryBuilders.termQuery("categoryId", courseSearchDTO.getCategoryId()));
            }
            //1.4添加课程状态搜索
            if (courseSearchDTO.getStatus() != null) {
                boolQuery.filter(QueryBuilders.termQuery("status", courseSearchDTO.getStatus()));
            }
            queryBuilder.withQuery(boolQuery);
            //2.添加排序条件
            if (StringUtils.isNotBlank(courseSearchDTO.getSortField()) && StringUtils.isNotBlank(courseSearchDTO.getSortOrder())) {
                if ("relevance".equals(courseSearchDTO.getSortField())) {
                    //使用_score排序
                    queryBuilder.withSort(SortBuilders.scoreSort()
                            .order("desc".equalsIgnoreCase(courseSearchDTO.getSortOrder()) ?
                                    SortOrder.DESC : SortOrder.ASC));
                } else {
                    SortOrder sortOrder = "desc".equalsIgnoreCase(courseSearchDTO.getSortOrder()) ?
                            SortOrder.DESC : SortOrder.ASC;
                    queryBuilder.withSort(SortBuilders.fieldSort(courseSearchDTO.getSortField())
                            .order(sortOrder));
                }
            } else {
                //2.1设置默认排序字段
                queryBuilder.withSort(SortBuilders.fieldSort("recommendWeight").order(SortOrder.DESC));
            }
        }
        //3.添加分页条件
        queryBuilder.withPageable(PageRequest.of(courseSearchDTO.getPage() - 1, courseSearchDTO.getSize()));
        //4.调用ES的client中的Search方法
        SearchHits<CourseDocument> searchHits = template.search(queryBuilder.build(), CourseDocument.class, IndexCoordinates.of("course"));
        //5.封装结果
        ArrayList<CourseDocument> courseDocuments = new ArrayList<>();
        searchHits.forEach(searchHit -> {
            CourseDocument courseDocument = searchHit.getContent();
            courseDocuments.add(courseDocument);
        });
        PageImpl<CourseDocument> pageCourseDocuments = new PageImpl<>(courseDocuments, PageRequest.of(courseSearchDTO.getPage() - 1, courseSearchDTO.getSize()), searchHits.getTotalHits());
        return pageCourseDocuments;

    }
}
